---------------------------------------------------------------
-- @Description：         service_gate
---------------------------------------------------------------
local skynet = require "skynet"
local netpack = require "skynet.netpack"
local socketdriver = require "skynet.socketdriver"
local log = require "sky_log"

local gateserver = {}
local queue		
local maxclient	
local client_number = 0
local CMD = setmetatable({}, { __gc = function() netpack.clear(queue) end })
local nodelay = false

local connection = {}
-- true : connected
-- nil : closed
-- false : close read

function gateserver.openclient(fd)
	if connection[fd] then
		socketdriver.start(fd)
		log.info("gateserver openclient fd ok! fd=", fd)
		return true
	end

	return false
end

function gateserver.closeclient(fd)
	local c = connection[fd]
	if c ~= nil then
		log.info("gateserver closeclient fd ok! fd=", fd)
		connection[fd] = nil
		socketdriver.close(fd)
	end
end

function gateserver.start(handler)
	assert(handler.message)
	assert(handler.connect)

	function CMD.initialization(source)
		maxclient = tonumber(skynet.getenv("max_nums"))
		log.warning("gate maxclient=", maxclient)
		if handler.initialization then
			return handler.initialization(source)
		end
	end

	function CMD.connect(source, _ip, _port, _agent)
		if not _ip 
		or not _port then 
			return 
		end	
		
		local fd = socketdriver.connect(_ip, _port)
		if client_number >= maxclient then
			socketdriver.shutdown(fd)
			return
		end
		if nodelay then
			socketdriver.nodelay(fd)
		end
		connection[fd] = true
		client_number = client_number + 1

		local msg = {
			ip = _ip,
			port = _port,
			client = fd,
			agent = _agent,
		}
		handler.connect(fd, msg)
		return msg
	end

	local MSG = {}

	local function dispatch_msg(fd, msg, sz)
		if connection[fd] then
			handler.message(fd, msg, sz)
		else
			log.info(string.format("Drop message from fd (%d) : %s", fd, netpack.tostring(msg,sz)))
		end
	end
	MSG.data = dispatch_msg

	local function dispatch_queue()
		local fd, msg, sz = netpack.pop(queue)
		if fd then
			skynet.fork(dispatch_queue)
			dispatch_msg(fd, msg, sz)
			for fd, msg, sz in netpack.pop, queue do
				dispatch_msg(fd, msg, sz)
			end
		end
	end
	MSG.more = dispatch_queue

	function MSG.close(fd)
		client_number = client_number - 1
		if connection[fd] then
			--close read
			connection[fd] = false	
		end
		if handler.disconnect then
			handler.disconnect(fd)
		end
	end

	function MSG.error(fd, msg)
		socketdriver.shutdown(fd)
		if handler.error then
			handler.error(fd, msg)
		end
	end

	function MSG.warning(fd, size)
		log.warning("MSG.warning(fd, size)")
		if handler.warning then
			handler.warning(fd, size)
		end
	end

	skynet.register_protocol {
		name = "socket",
		id = skynet.PTYPE_SOCKET,	-- PTYPE_SOCKET = 6
		unpack = function ( msg, sz )
			return netpack.filter( queue, msg, sz)
		end,
		dispatch = function (_, _, q, type, ...)
			queue = q
			if type then
				MSG[type](...)
			end
		end
	}

	local function init()
		skynet.dispatch("lua", function (_, address, cmd, ...)
			local f = CMD[cmd]
			if f then
				skynet.ret(skynet.pack(f(address, ...)))
			else
				skynet.ret(skynet.pack(handler.command(cmd, address, ...)))
			end

		end)
	end

	if handler.embed then
		init()
	else
		skynet.start(init)
	end
end

return gateserver
